技巧93 限制能力
正如我们提到的,容器上的root用户和宿主机上的root用户是一样的。但是不是所有的root用户都生而平等。Linux允许为root用户分配进程级别的更加细粒度的权限。
这些细粒度的权限被称为 能力 (capability),这样即使是root用户,也能限制他们所能做的破坏。本技巧展示了当运行Docker容器的时候如何操纵这些能力,特别是在不完全信任其内容的时候。
问题
想要降低容器在宿主机上进行破坏性活动的能力。
解决方案
使用 --drop-cap
标志减少容器可以获得的能力。
1.Unix信任模型
为了了解“减少能力”的含义和作用,需要一点儿背景知识。当Unix系统被设计出来的时候,其信任模型并不复杂。存在被信任的管理员(root用户)和不被信任的用户。root用户可以做任何事情,然而普通用户只能影响他们自己的文件。因为这个操作系统一般是在大学实验室里使用而且本身不大,所以这个模型还是合理的。
随着Unix模型的发展以及互联网的到来,这个模型越来越不合理了。类似网络服务器的程序需要root权限来在80端口提供内容,同时它们也作为在宿主机上执行命令的有效代理。针对这些情况有些标准的应对模式,例如,绑定到端口80并把有效用户ID赋予一个非root用户。扮演着不同角色的用户,从系统管理员到数据库管理员,直到应用支持工程师和开发者,可能都需要对不同的系统上的资源有细粒度的访问权限。Unix用户组从某种程度上减轻了这个问题,但是正如任何系统管理员都会说的那样,为这些权限需求建模并不是一个小问题。
2.Linux能力
为了尝试支持一个更加细粒度的对用户权限进行管理的方式,Linux内核工程师们开发了 能力 (capability)。它尝试把单块的root权限拆解成各个可以独立授予的功能片段。读者可以通过执行 man 7 capabilities
来查看更多细节(假设安装了帮助手册)。
有用的是,Docker默认关闭了一些特定的能力。也就是说,即使在容器内有root权限,有些事也做不了。例如,允许影响网络栈的 CAP_NET_ADMIN
能力,默认就被禁用了。
表14-1列出了Linux的各项能力,给出了对它们允许做的事情的简要介绍,并且标明了它们是否在Docker容器内默认开启。
能 力 | 描 述 | 开启与否 |
---|---|---|
CHOWN |
对任意文件进行所有权更改 | 是 |
DAC_OVERRIDE |
覆写读、写和执行权限检查 | 是 |
FSETID |
当修改文件时,不清除suid和guid位 | 是 |
FOWNER |
存储文件时,覆写所有权检查 | 是 |
KILL |
对于信号,绕过权限检查 | 是 |
MKNOD |
使用 mknod 来创建特殊文件 |
是 |
NET_RAW |
使用原始套接字和分组套接字,并且绑定到端口以进行透明代理 | 是 |
SETGID |
对进程的组所有权进行更改 | 是 |
SETUID |
对进程的用户所有权进行更改 | 是 |
SETFCAP |
设定文件能力 | 是 |
SETPCAP |
如果不支持文件能力,那么对来自其他进程和发往其他进程的能力进行限制 | 是 |
NET_BIND_SERVICE |
绑定套接字到小于1024的端口 | 是 |
SYS_CHROOT |
使用 chroot |
是 |
AUDIT_WRITE |
写入内核日志 | 是 |
AUDIT_CONTROL |
启用/禁用内核日志记录 | 否 |
BLOCK_SUSPEND |
使用能阻止系统中止的特性 | 否 |
DAC_READ_SEARCH |
绕过读取文件和目录时的权限检查 | 否 |
IPC_LOCK |
锁定内存 | 否 |
IPC_OWNER |
绕过进程间通信对象权限 | 否 |
LEASE |
在一般文件上建立租约(对试图打开或删除的监控) | 否 |
LINUX_IMMUTABLE |
设立i-node标志 FS_APPEND_FL 和 FS_IMMUTABLE_FL |
否 |
MAC_ADMIN |
重载强制访问控制(和Smack Linux安全模组(SLM)有关) | 否 |
MAC_OVERRIDE |
改变强制访问控制(和SLM有关) | 否 |
NET_ADMIN |
各种网络相关的操作,包括IP防火墙改变和接口配置 | 否 |
NET_BROADCAST |
不再使用 | 否 |
SYS_ADMIN |
一系列管理员功能。查看 man capabilities 获取更多信息 |
否 |
SYS_BOOT |
重启 | 否 |
SYS_MODULE |
加载/卸载内核模块 | 否 |
SYS_NICE |
操纵进程的nice优先级 | 否 |
SYS_PACCT |
开启或关闭进程记账 | 否 |
SYS_PTRACE |
追踪进程的系统调用以及其他进程操纵能力 | 否 |
SYS_RAWIO |
对系统很多核心部分进行输入/输出,如内存和SCSI设备命令 | 否 |
SYS_RESOURCE |
控制和重载多种资源限制 | 否 |
SYS_TIME |
设置系统时钟 | 否 |
SYS_TTY_CONFIG |
在虚拟终端上的特权操作 | 否 |
注意
如果读者使用的不是Docker默认的容器引擎(libcontainer),这些能力可能在读者安装的软件上有所不同。如果有系统管理员,可以去请教他们,确认这些能力。
但是,内核维护者仅在系统内分配了32个能力,所以这些能力都拓展了自己的范围,同时越来越多的细粒度root权限在内核外被创造出来。最值得一提的是,命名模糊的 CAP_SYS_ADMIN
能力涵盖了从改变宿主机域名到超出系统范围内打开文件数量的上限等多种不同行为。
一种极端的做法是从容器内移除所有在Docker默认开启的能力,然后看一下什么不工作了。在此我们运行一个移除所有默认开启的能力的bash脚本:
$ docker run -ti --cap-drop=CHOWN --cap-drop=DAC_OVERRIDE \
--cap-drop=FSETID --cap-drop=FOWNER --cap-drop=KILL --cap-drop=MKNOD \
--cap-drop=NET_RAW --cap-drop=SETGID --cap-drop=SETUID \
--cap-drop=SETFCAP --cap-drop=SETPCAP --cap-drop=NET_BIND_SERVICE \
--cap-drop=SYS_CHROOT --cap-drop=AUDIT_WRITE debian /bin/bash
如果通过这个shell来运行程序,可以看到它是在什么地方如期失败,然后重新加上所需的能力。例如,用户可能需要改变文件所有权的能力,那么在前述的代码中就不能去掉 FOWNER
能力:
$ docker run -ti --cap-drop=CHOWN --cap-drop=DAC_OVERRIDE \
--cap-drop=FSETID --cap-drop=KILL --cap-drop=MKNOD \
--cap-drop=NET_RAW --cap-drop=SETGID --cap-drop=SETUID \
--cap-drop=SETFCAP --cap-drop=SETPCAP --cap-drop=NET_BIND_SERVICE \
--cap-drop=SYS_CHROOT --cap-drop=AUDIT_WRITE debian /bin/bash
提示
如果想要启用或禁用所有的能力,可以使用 all
而不是某个特定的能力,如 docker run -ti --cap-drop=all ubuntu bash。
讨论
如果在bash shell中以禁用所有能力来执行一些基本的命令,会发现这很有用。尽管在运行一些更复杂的程序的时候得到的好处可能会有所不同。
警告
值得澄清的是,这些能力中的很大一部分是和影响系统上其他用户的对象的root能力相关的,而不是root自己的对象。一个root用户仍然能够使用 chown
命令改变宿主机上root文件的所有权。例如,假设他们是在容器内操作并且通过卷挂载的方式能够访问宿主机的文件。因此,纵使所有这些能力都关闭了,仍然值得把应用程序降级为一个非root用户。
这种对容器的能力的调优能力意味着对 docker run
使用 --privileged
标志就不必要了。需要能力的进程会被审计并且处于宿主机管理员的控制之下。